﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using Machine.Specifications;
using Rhino.Mocks;
using Webshop20.Domain.Catalog.Core;
using Webshop20.Domain.Common.Exceptions;
using Webshop20.Domain.Shared.Products.Core;
using Webshop20.UnitTests.Builder;

namespace Webshop20.Specifications
{

    #region contexts

    public abstract class given_empty_shopping_cart
    {

        public static ShoppingCart shoppingCart;
        public static Product productToAdd;
        public static DateTime addedAt;

        Establish context_for_all = () =>
        {
            shoppingCart = new ShoppingCart();
            productToAdd = new BuildProduct();
            addedAt = DateTime.Now;
        };
    }

    public abstract class given_shopping_cart_with_one_product : given_empty_shopping_cart
    {
        Establish context = () => shoppingCart.AddProduct(productToAdd, addedAt);
    }

    public abstract class given_shopping_cart_with_on_item_on_saved_items_list : given_empty_shopping_cart
    {
        Establish context = () => shoppingCart.AddProductToSavedItemList(productToAdd);
    }

    #endregion

    [Subject(typeof(ShoppingCart))]
    public class when_the_customer_adds_a_product : given_empty_shopping_cart
    {
        public static ShoppingCartItem newShoppingCartItem;

        Because of = () => newShoppingCartItem = shoppingCart.AddProduct(productToAdd, addedAt);

        It should_contain_a_position_with_this_product = 
            () => shoppingCart.CartItems.FirstOrDefault().Product.ShouldEqual(productToAdd);

        It should_contain_a_position_with_quantity_one = 
            () => shoppingCart.CartItems.FirstOrDefault().Quantity.ShouldEqual(1);

        It should_return_new_item_with_product_and_date =
            () =>
                {
                    newShoppingCartItem.Product.ShouldBeTheSameAs(productToAdd);
                    newShoppingCartItem.AddedAt.ShouldEqual(addedAt);
                };
    }

    [Subject(typeof(ShoppingCart))]
    public class when_the_customer_adds_another_item_of_the_product : given_shopping_cart_with_one_product
    {
        Because of = () => shoppingCart.AddProduct(productToAdd, addedAt);

        It should_should_increase_the_quantity_by_one = () => shoppingCart.CartItems.FirstOrDefault().Quantity.ShouldEqual(2);
    }

    [Subject(typeof(ShoppingCart))]
    public class when_the_customer_removes_a_product : given_shopping_cart_with_one_product
    {
        Because of = () => shoppingCart.RemoveCartItemFor(productToAdd);

        It should_be_empty = () => shoppingCart.CartItems.Count().ShouldEqual(0);
    }

    [Subject(typeof(ShoppingCart))]
    public class when_the_shopping_cart_is_cleared : given_shopping_cart_with_one_product
    {
        Because of = () => shoppingCart.Clear();

        It should_be_empty = () => shoppingCart.CartItems.Count().ShouldEqual(0);
    }

    [Subject(typeof(ShoppingCart))]
    public class when_the_quantity_for_an_shopping_cart_item_is_being_changed : given_shopping_cart_with_one_product
    {
         static int newQuantity = 10;

         Because of = () => shoppingCart.ChangeQuantity(productToAdd, newQuantity);



         It should_change_the_quantity_property_of_the_item_to_new_quantity =
             () => shoppingCart.CartItems.FirstOrDefault().Quantity.ShouldEqual(newQuantity);

    }

    [Subject(typeof(ShoppingCart))]
    public class when_the_quantity_for_shopping_cart_item_is_negative : given_shopping_cart_with_one_product
    {
        public static Exception domainException;
        public static int negativeQuantity = -10;

        Because of = () => domainException = Catch.Exception(() => shoppingCart.ChangeQuantity(productToAdd, negativeQuantity));

        It should_not_allow_to_change_the_quantity = () => domainException.ShouldBeOfType<DomainException>(); 

    }

    [Subject(typeof(ShoppingCart))]
    public class when_the_shopping_cart_is_merged_with_another : given_shopping_cart_with_one_product
    {
        public static ShoppingCart anotherShoppingCart = new BuildShoppingCart();

        Because of = () => shoppingCart.MergeWith(anotherShoppingCart);

        It should_add_items_from_the_other_shopping_cart =
            () => shoppingCart.CartItems.FirstOrDefault().Quantity.ShouldEqual(2);
        
    }

    [Subject(typeof(ShoppingCart))]
    public class when_a_product_is_added_to_the_saved_items_list : given_empty_shopping_cart
    {
        Because of = () => shoppingCart.AddProductToSavedItemList(productToAdd);

        It should_have_one_item_in_saved_items_list = 
            () => shoppingCart.SavedItems.Count().ShouldEqual(1);
    }

    [Subject(typeof(ShoppingCart))]
    public class when_an_item_is_removed_from_saved_items_list : given_shopping_cart_with_on_item_on_saved_items_list
    {
        Because of = () => shoppingCart.RemoveProductToSavedItemList(productToAdd);

        It should_not_have_an_item_on_saved_items_list = () => shoppingCart.SavedItems.Count().ShouldEqual(0);
    }

    [Subject(typeof(ShoppingCart))]
    public class when_an_item_is_moved_form_cart_items_list_to_saved_items_list : given_shopping_cart_with_one_product
    {
        Because of = () => shoppingCart.MoveProductFromCartItemListToSavedItemList(productToAdd);

        It should_have_one_item_on_saved_items_list = () => shoppingCart.SavedItems.Count().ShouldEqual(1);

        It should_have_no_item_on_cart_items_list = () => shoppingCart.CartItems.Count().ShouldEqual(0);
    }

    [Subject(typeof(ShoppingCart))]
    public class when_total_price_is_calculated : given_shopping_cart_with_one_product
    {
        private static ISalesTax salesTax;
        private static decimal total;

        Establish context = () =>
                                        {
                                            shoppingCart.ChangeQuantity(productToAdd, 2);
                                            salesTax = MockRepository.GenerateMock<ISalesTax>();
                                            salesTax.Stub(x => x.Calculate(14.00m)).Return(1).IgnoreArguments().Repeat.Any();
                                        };

        Because of = () => total = shoppingCart.CalculateTotal(salesTax, addedAt);

        It should_return_the_price_for_the_wohle_cart_content =
            () => (productToAdd.CalculateTotal(salesTax, addedAt)*2).ShouldEqual(total);
    }



}
